How to Write Better TypeScript Code: Best Practices for Clean, Effective, and Scalable Code
https://dev.to/yugjadvani/how-to-write-better-typescript-code-best-practices-for-clean-effective-and-scalable-code-38d2
typeとinterfaceを使い分ける
interfaceはオブジェクト、それ以外はtype。
code:ts
interface User {
id: number;
name: string;
email: string;
}
type Status = "active" | "inactive" | "pending";
https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#differences-between-type-aliases-and-interfaces
https://zenn.dev/luvmini511/articles/6c6f69481c2d17
なぜinterfaceはオブジェクト?
Microsoftでは、Performance の観点でinterfaceがコンパイルが速いとのこと
戻り値は明確に定義
コードがより予測しやすくなる上に、関数の動作が後で変更された場合にエラーが検知しやすくなる。
code:ts
function getUser(id: number): User | null {
// logic to fetch user
}
Null と Undefined を安全に処理する
nullやUndefinedでも安全にデータにアクセスできるようにする。
code:ts
type User = {
profile?: {
name?: string;
};
};
const user: User = {}; // API から取得したデータ(profile がない場合もある)
const userName = user?.profile?.name ?? "Guest";
console.log(userName); // "Guest"
Enumを活用する
TypeScriptの列挙型では、名前付き定数のセットを定義できる。 これによってコードの表現力が増し、任意の文字列や数値の使用を防ぐことができる。
列挙型は、固定されたオプションのセットを扱うときに特に有用で、コードを自己文書化し、エラーを減らすことができる。
code:ts
enum UserRole {
Admin = "ADMIN",
User = "USER",
Guest = "GUEST",
}
function assignRole(role: UserRole) {
// logic here
}
と思いきや、Enumは使わないようがいいという意見も
https://www.totaltypescript.com/why-i-dont-like-typescript-enums
サンプルコード
✅ enum を使うべきケース
API のレスポンスなど固定値を扱う場合
Admin | User | Guest のような役割を enum で定義
数値の定数管理(ビットフラグ)
enum の数値をビット演算に使う場合
保守性が重要なプロジェクト
明示的に Status.Success のように書けると、可読性が上がる
❌ union型 を使ったほうがよいケース
文字列の定数(Status = "success" | "error" | "pending" など)
追加の JavaScript コードが不要で、トランスパイル後もシンプル
特に enum の利点を活かさない場合
シンプルな定数なら union型 で十分
バンドルサイズを抑えたい場合
enum だと余計なオブジェクトが増える
徹底的なチェックにはneverを使う
予めて定義した以外の型が来た場合にエラーとしてスローできる。
このnever型チェック手法により、未処理のケースのリスクが軽減され、コードが網羅的かつ型安全であることが保証される。
※neverの参考
https://qiita.com/macololidoll/items/1c948c1f1acb4db6459e
code:ts
type Shape = Circle | Square | Triangle;
function getArea(shape: Shape) {
switch (shape.type) {
case "circle":
return Math.PI * shape.radius ** 2;
case "square":
return shape.side * shape.side;
default:
const _exhaustiveCheck: never = shape;
return _exhaustiveCheck;
}
}